import datetime
from datetime import timedelta

from django.core.exceptions import ValidationError, MultipleObjectsReturned
from django.db import models
from django.contrib.auth.models import User
from django.db.models import signals
from django.utils.translation import gettext_lazy as _
from six import python_2_unicode_compatible

from celery import schedules
# from celery import states
# from celery.events.state import heartbeat_expires


def cronexp(field):
    return field and str(field).replace(' ', '') or '*'


class Sign(models.Model):
    sign_id = models.AutoField(primary_key=True, null=False)
    sign_name = models.CharField(max_length=100)
    sign_type = models.CharField(max_length=100, default="无")
    description = models.CharField(max_length=200)
    update_time = models.DateTimeField('更新时间', auto_now=True)
    update_user = models.CharField(max_length=30, default='')

    def __str__(self):
        return self.sign_name

    class Meta:
        verbose_name = "签名管理"
        verbose_name_plural = "签名管理"


class Project(models.Model):
    prj_id = models.AutoField(primary_key=True, null=False)
    prj_name = models.CharField(max_length=100)
    user = models.ForeignKey(User, on_delete=models.CASCADE, default='')
    sign = models.ForeignKey(Sign, on_delete=models.CASCADE, default='')
    description = models.CharField(max_length=200)
    update_time = models.DateTimeField('更新时间', auto_now=True)

    def __str__(self):
        return self.prj_name

    class Meta:
        verbose_name = "项目管理"
        verbose_name_plural = "项目管理"


class Environment(models.Model):
    env_id = models.AutoField(primary_key=True, null=False)
    env_name = models.CharField(max_length=100)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    description = models.CharField(max_length=200)
    url = models.CharField(max_length=100)
    import_url = models.CharField(max_length=100, default="")
    is_swagger = models.IntegerField(default=0)  # 导入swagger
    set_headers = models.TextField(default='')  # 设置默认headers
    private_key = models.CharField(max_length=100)
    update_time = models.DateTimeField('更新时间', auto_now=True)
    update_user = models.CharField(max_length=30, default='')

    def __str__(self):
        return self.env_name

    class Meta:
        verbose_name = "测试环境"
        verbose_name_plural = "测试环境"


class Interface(models.Model):
    if_id = models.AutoField(primary_key=True, null=False)
    if_name = models.CharField(max_length=100)
    url = models.CharField(max_length=100)
    method = models.CharField(max_length=10)
    data_type = models.CharField(max_length=10)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    is_sign = models.IntegerField()
    is_header = models.IntegerField(default=0)  # 标记设置header接口
    set_mock = models.TextField(default='')  # 设置mock
    skip = models.TextField(default='')  # 跳过
    description = models.CharField(max_length=200)
    request_header_param = models.TextField()
    request_body_param = models.TextField()
    response_header_param = models.TextField()
    response_body_param = models.TextField()
    update_time = models.DateTimeField('更新时间', auto_now=True)
    update_user = models.CharField(max_length=30, default='')

    def __str__(self):
        return self.if_name

    class Meta:
        verbose_name = "接口管理"
        verbose_name_plural = "接口管理"


class Case(models.Model):
    case_id = models.AutoField(primary_key=True, null=False)
    case_name = models.CharField(max_length=100)
    weight = models.IntegerField(default=1)  # 权重
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    description = models.CharField(max_length=200)
    content = models.TextField()
    update_time = models.DateTimeField('更新时间', auto_now=True)
    update_user = models.CharField(max_length=30, default='')

    def __str__(self):
        return self.case_name

    class Meta:
        verbose_name = "测试用例"
        verbose_name_plural = "测试用例"


class Plan(models.Model):
    plan_id = models.AutoField(primary_key=True, null=False)
    plan_name = models.CharField(max_length=100)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    environment = models.ForeignKey(Environment, on_delete=models.CASCADE)
    description = models.CharField(max_length=200)
    content = models.TextField()
    report_name = models.CharField(max_length=255, default="")
    make = models.IntegerField(null=True)
    is_locust = models.IntegerField(default=0)  # 性能测试
    is_task = models.IntegerField(default=0)  # 定时任务
    update_time = models.DateTimeField('更新时间', auto_now=True)
    update_user = models.CharField(max_length=30, default='')

    def __str__(self):
        return self.plan_name

    class Meta:
        verbose_name = "测试计划"
        verbose_name_plural = "测试计划"


class Report(models.Model):
    report_id = models.AutoField(primary_key=True, null=False)
    report_name = models.CharField(max_length=255)
    report_path = models.CharField(max_length=255, default='', null=True)
    pic_name = models.CharField(max_length=255, default='')
    totalTime = models.CharField(max_length=50, default='')
    startTime = models.CharField(max_length=50, default='')
    plan = models.ForeignKey(Plan, on_delete=models.CASCADE, null=True)
    task = models.IntegerField(null=True)
    content = models.TextField()
    case_num = models.IntegerField(null=True)
    skip_num = models.IntegerField(null=True)
    pass_num = models.IntegerField(null=True)
    fail_num = models.IntegerField(null=True)
    error_num = models.IntegerField(null=True)
    update_time = models.DateTimeField('更新时间', auto_now=True)
    update_user = models.CharField(max_length=30, default='')
    make = models.IntegerField(null=True)

    def __str__(self):
        return self.report_name

    class Meta:
        verbose_name = "测试报告"
        verbose_name_plural = "测试报告"


# 发布会表
class Event(models.Model):
    name = models.CharField(max_length=100)  # 发布会标题
    limit = models.IntegerField()  # 参加人数
    status = models.BooleanField()  # 状态
    address = models.CharField(max_length=200)  # 地址
    start_time = models.DateTimeField('events time')  # 发布会时间
    create_time = models.DateTimeField(auto_now=True)  # 创建时间(自动获取当前时间)

    # admin中显示发布会名称
    def __str__(self):
        return self.name

    class Meta:
        verbose_name = "发布会表"
        verbose_name_plural = "发布会表"


# 嘉宾表
class Guest(models.Model):
    event = models.ForeignKey(Event, related_name='guests', on_delete=models.CASCADE)  # 关联发布会id
    realname = models.CharField(max_length=64)  # 姓名
    phone = models.CharField(max_length=16)  # 手机号
    email = models.EmailField()  # 邮箱
    sign = models.BooleanField()  # 签到状态
    create_time = models.DateTimeField(auto_now=True)  # 创建时间(自动获取当前时间)

    class Meta:
        unique_together = ('event', 'phone')
        verbose_name = "嘉宾表"
        verbose_name_plural = "嘉宾表"

    # admin中显示嘉宾名称
    def __str__(self):
        return self.realname


# locust报告
class LocustReport(models.Model):
    id = models.AutoField(primary_key=True, null=False)
    current_response_time_percentile_50 = models.FloatField(default=0.0, null=True)
    current_response_time_percentile_95 = models.FloatField(default=0.0, null=True)
    fail_ratio = models.FloatField(default=0.0, null=True)
    total_rps = models.FloatField(default=0.0, null=True)
    user_count = models.IntegerField(default=0, null=True)
    errors = models.TextField()
    slaves = models.TextField()
    stats = models.TextField()
    state = models.CharField(max_length=50, default='')
    update_time = models.DateTimeField('更新时间', auto_now=True)
    update_user = models.CharField(max_length=30, default='')

    class Meta:
        verbose_name = "locust报告"
        verbose_name_plural = "locust报告"


class DebugTalk(models.Model):
    create_time = models.DateTimeField('创建时间', auto_now_add=True)
    update_time = models.DateTimeField('更新时间', auto_now=True)
    belong_project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
    debugtalk = models.TextField(null=True, default='#debugtalk.py')
    status = models.BooleanField(default=False)  # 状态
    page = models.IntegerField(default=1, null=True)

    class Meta:
        verbose_name = '驱动py文件'
        db_table = 'DebugTalk'


class ModularTable(models.Model):
    url = models.CharField(max_length=255)
    Icon = models.CharField(max_length=255)
    model_name = models.CharField(max_length=255)

    def __str__(self):
        return self.model_name

    class Meta:
        verbose_name = "模块名称"
        verbose_name_plural = "模块名称"


class UserPower(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    power = models.CharField(max_length=255)

    def __str__(self):
        return self.user

    class Meta:
        verbose_name = "用户权限"
        verbose_name_plural = "用户权限"


@python_2_unicode_compatible
class CrontabSchedule(models.Model):
    minute = models.CharField(_('minute'), max_length=64, default='*')
    hour = models.CharField(_('hour'), max_length=64, default='*')
    day_of_week = models.CharField(
        _('day of week'), max_length=64, default='*',
    )
    day_of_month = models.CharField(
        _('day of month'), max_length=64, default='*',
    )
    month_of_year = models.CharField(
        _('month of year'), max_length=64, default='*',
    )

    class Meta:
        verbose_name = _('crontab')
        verbose_name_plural = _('crontabs')
        ordering = ['month_of_year', 'day_of_month',
                    'day_of_week', 'hour', 'minute']

    def __str__(self):
        return '{0} {1} {2} {3} {4} (m/h/d/dM/MY)'.format(
            cronexp(self.minute),
            cronexp(self.hour),
            cronexp(self.day_of_week),
            cronexp(self.day_of_month),
            cronexp(self.month_of_year),
        )

    @property
    def schedule(self):
        return schedules.crontab(minute=self.minute,
                                 hour=self.hour,
                                 day_of_week=self.day_of_week,
                                 day_of_month=self.day_of_month,
                                 month_of_year=self.month_of_year)

    @classmethod
    def from_schedule(cls, schedule):
        spec = {'minute': schedule._orig_minute,
                'hour': schedule._orig_hour,
                'day_of_week': schedule._orig_day_of_week,
                'day_of_month': schedule._orig_day_of_month,
                'month_of_year': schedule._orig_month_of_year}
        try:
            return cls.objects.get(**spec)
        except cls.DoesNotExist:
            return cls(**spec)
        except MultipleObjectsReturned:
            cls.objects.filter(**spec).delete()
            return cls(**spec)


@python_2_unicode_compatible
class IntervalSchedule(models.Model):
    PERIOD_CHOICES = (('days', _('Days')),
                      ('hours', _('Hours')),
                      ('minutes', _('Minutes')),
                      ('seconds', _('Seconds')),
                      ('microseconds', _('Microseconds')))

    every = models.IntegerField(_('every'), null=False)
    period = models.CharField(
        _('period'), max_length=24, choices=PERIOD_CHOICES,
    )

    class Meta:
        verbose_name = _('interval')
        verbose_name_plural = _('intervals')
        ordering = ['period', 'every']

    @property
    def schedule(self):
        return schedules.schedule(timedelta(**{self.period: self.every}))

    @classmethod
    def from_schedule(cls, schedule, period='seconds'):
        every = max(schedule.run_every.total_seconds(), 0)
        try:
            return cls.objects.get(every=every, period=period)
        except cls.DoesNotExist:
            return cls(every=every, period=period)
        except MultipleObjectsReturned:
            cls.objects.filter(every=every, period=period).delete()
            return cls(every=every, period=period)

    def __str__(self):
        if self.every == 1:
            return _('every {0.period_singular}').format(self)
        return _('every {0.every:d} {0.period}').format(self)

    @property
    def period_singular(self):
        return self.period[:-1]


class PeriodicTasks(models.Model):
    ident = models.SmallIntegerField(default=1, primary_key=True, unique=True)
    last_update = models.DateTimeField(null=False)

    # objects = managers.ExtendedManager()

    @classmethod
    def changed(cls, instance):
        if not instance.no_changes:
            cls.objects.update_or_create(ident=1,
                                         defaults={'last_update': datetime.datetime.now()})

    @classmethod
    def last_change(cls):
        try:
            return cls.objects.get(ident=1).last_update
        except cls.DoesNotExist:
            pass


@python_2_unicode_compatible
class PeriodicTask(models.Model):
    name = models.CharField(
        _('name'), max_length=200, unique=True,
        help_text=_('Useful description'),
    )
    task = models.CharField(_('task name'), max_length=200)
    interval = models.ForeignKey(
        IntervalSchedule,
        null=True, blank=True, verbose_name=_('interval'),
        on_delete=models.CASCADE,
    )
    crontab = models.ForeignKey(
        CrontabSchedule, null=True, blank=True, verbose_name=_('crontab'),
        on_delete=models.CASCADE,
        help_text=_('Use one of interval/crontab'),
    )
    args = models.TextField(
        _('Arguments'), blank=True, default='[]',
        help_text=_('JSON encoded positional arguments'),
    )
    kwargs = models.TextField(
        _('Keyword arguments'), blank=True, default='{}',
        help_text=_('JSON encoded keyword arguments'),
    )
    queue = models.CharField(
        _('queue'), max_length=200, blank=True, null=True, default=None,
        help_text=_('Queue defined in CELERY_QUEUES'),
    )
    exchange = models.CharField(
        _('exchange'), max_length=200, blank=True, null=True, default=None,
    )
    routing_key = models.CharField(
        _('routing key'), max_length=200, blank=True, null=True, default=None,
    )
    expires = models.DateTimeField(
        _('expires'), blank=True, null=True,
    )
    enabled = models.BooleanField(
        _('enabled'), default=True,
    )
    last_run_at = models.DateTimeField(
        auto_now=False, auto_now_add=False,
        editable=False, blank=True, null=True,
    )
    total_run_count = models.PositiveIntegerField(
        default=0, editable=False,
    )
    date_changed = models.DateTimeField(auto_now=True)
    description = models.TextField(_('description'), blank=True)

    # objects = managers.PeriodicTaskManager()
    no_changes = False

    class Meta:
        verbose_name = _('periodic task')
        verbose_name_plural = _('periodic tasks')

    def validate_unique(self, *args, **kwargs):
        super(PeriodicTask, self).validate_unique(*args, **kwargs)
        if not self.interval and not self.crontab:
            raise ValidationError(
                {'interval': ['One of interval or crontab must be set.']})
        if self.interval and self.crontab:
            raise ValidationError(
                {'crontab': ['Only one of interval or crontab must be set']})

    def save(self, *args, **kwargs):
        self.exchange = self.exchange or None
        self.routing_key = self.routing_key or None
        self.queue = self.queue or None
        if not self.enabled:
            self.last_run_at = None
        super(PeriodicTask, self).save(*args, **kwargs)

    def __str__(self):
        fmt = '{0.name}: {{no schedule}}'
        if self.interval:
            fmt = '{0.name}: {0.interval}'
        if self.crontab:
            fmt = '{0.name}: {0.crontab}'
        return fmt.format(self)

    @property
    def schedule(self):
        if self.interval:
            return self.interval.schedule
        if self.crontab:
            return self.crontab.schedule

    def natural_key(self):
        return (self.name, )


signals.pre_delete.connect(PeriodicTasks.changed, sender=PeriodicTask)
signals.pre_save.connect(PeriodicTasks.changed, sender=PeriodicTask)
